<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2020/2/28
 * Time: 18:53
 */

namespace SwoStar\Server;

use Swoole\Coroutine\Http\Client;

use SwoStar\RPC\Rpc;
use SwoStar\Supper\Inotify;
use Swoole\Server as SwooleServer;
use SwoStar\Foundation\Application;
use Redis;

/**
 * 所有服务的父类， 写一写公共的操作
 */
abstract class Server
{
    //属性
    protected $swooleServer;

    protected $app;

    protected $inotify = null;

    protected $port=9000;

    protected $host='0.0.0.0';

    protected $watchFile=false;

    protected $redis;

    /**
     * 用于记录系统pid的信息
     * @var string
     */
    protected $pidFile = "/runtime/swostar.pid";
    /**
     * 这是swoole服务的配置
     * @var [type]
     */
    protected $config=[
        'task_worker_num' => 0,
    ];

    /**
     * 用于记录pid的信息
     * @var array
     */
    protected $pidMap = [
        'masterPid'  => 0,
        'managerPid' => 0,
        'workerPids' => [],
        'taskPids'   => []
    ];

    //自定义回调函数
    protected $event=[
        'server' => [
            //事件  以及事件函数
            "start"        => "onStart",
            "managerStart" => "onManagerStart",
            "managerStop"  => "onManagerStop",
            "shutdown"     => "onShutdown",
            "workerStart"  => "onWorkerStart",
            "workerStop"   => "onWorkerStop",
            "workerError"  => "onWorkerError",
        ],
        'sub' => [

        ],
        'ext'=>[]
    ];

    //新增一个initSetting方法设置port与host再修改子类
    protected abstract function initSetting();

    public function __construct(Application $app)
    {
        $this->app=$app;

        //初始化swoole配置
        $this->initSetting();
        //1.创建服务
        $this->createServer();

        //3.设置需要注册的回调函数--绑定回调函数
        $this->initEvent();
        //4.设置swoole的回调函数 --调用回调函数
        $this->setSwooleEvent();


    }

    //通用方法
    public function start()
    {
        $config=app('config');
        // 2. 设置配置信息
        $this->swooleServer->set($this->config);
        //判断是否启动rpc
        if($config->get('server.http.tcpable'))
        {
            new Rpc($this->swooleServer,$config->get('server.http.rpc'));
        }

        // 5. 启动
        $this->swooleServer->start();

    }

    //指定给某一个服务器连接发送信息
    public function send($ip,$port,$data,$header=null)
    {
        $cli= new Client($ip,$port);
        empty($header)?:$cli->setHeaders($header);

        if($cli->upgrade('/')){
            $cli->push(\json_encode($data));
        }
    }
    /**
     * 创建服务
     * 六星教育 @shineyork老师
     */
    protected abstract function createServer();
    /**
     * 初始化监听的事件
     * 六星教育 @shineyork老师
     */
    protected abstract function initEvent();

    /**
     * 设置swoole的回调事件
     * 六星教育 @shineyork老师
     */
    protected function setSwooleEvent()
    {
        foreach ($this->event as $type => $events) {
            foreach ($events as $event => $func) {
                $this->swooleServer->on($event, [$this, $func]);
            }
        }
    }

    protected function watchEvent()
    {
        return function($event){
            $action = 'file:';
            switch ($event['mask']) {
                case IN_CREATE:
                    $action = 'IN_CREATE';
                    break;

                case IN_DELETE:
                    $action = 'IN_DELETE';
                    break;
                case \IN_MODIFY:
                    $action = 'IN_MODIF';
                    break;
                case \IN_MOVE:
                    $action = 'IN_MOVE';
                    break;
            }

           $this->swooleServer->reload();
        };
    }
    //回调方法

    public function onStart(SwooleServer $server)
    {
        $this->pidMap['masterPid']=$server->master_pid;
        $this->pidMap['managerPid']=$server->manager_pid;

        // 保存PID到文件里面
          $pidStr = \sprintf('%s,%s', $server->master_pid, $server->manager_pid);
          \file_put_contents(app()->getBasePath().$this->pidFile, $pidStr);


        if ($this->watchFile ) {
            $this->inotify = new Inotify($this->app->getBasePath(), $this->watchEvent());
            $this->inotify->start();
        }

        //im-server向route请求注册信息
        $this->app->make('event')->trigger('start',[$this]);

    }

    public function onManagerStart(SwooleServer $server)
    {

    }
    public function onManagerStop(SwooleServer $server)
    {

    }
    public function onShutdown(SwooleServer $server)
    {

    }
    public function onWorkerStart(SwooleServer $server, int $worker_id)
    {
        $this->pidMap['workerPids'] = [
            'id'  => $worker_id,
            'pid' => $server->worker_id
        ];

        $this->redis=new Redis;
        $this->redis->pconnect($this->app->make('config')->get('database.redis.host'),
            $this->app->make('config')->get('database.redis.port'));
        $this->redis->auth($this->app->make('config')->get('database.redis.password'));
    }



    public function onWorkerStop(SwooleServer $server, int $worker_id)
    {

    }
    public function onWorkerError(SwooleServer $server, int $workerId, int $workerPid, int $exitCode, int $signal)
    {

    }

    public function setEvent($type, $event)
    {
        // 暂时不支持直接设置系统的回调事件
        if ($type == "server") {
            return $this;
        }
        $this->event[$type] = $event;
        return $this;
    }

    /**
     * @return array
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * @param array $config
     *
     * @return static
     */
    public function setConfig($config)
    {
        $this->config = array_map($this->config, $config);
        return $this;
    }

    public function setWatchFile($watchFile){
        $this->watchFile=$watchFile;
    }

    public function getPort(){
        return $this->port;
    }

    public function getHost(){
        return $this->host;
    }

    public function getRedis(){
        return $this->redis;
    }
}